package com.validators;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.List;

import javax.faces.context.FacesContext;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.apache.log4j.Logger;
import org.springframework.web.jsf.FacesContextUtils;

import com.common.BaseModel;
import com.dao.BaseModelDAO;
import com.view.BaseView;

public class DateRangeValidator implements
		ConstraintValidator<DateRange, BaseView> {
	private String start;
	private String end;
	private BaseModelDAO<?> dao;
	private Class<?> entityClass;
	private String daoName;
	private String tipo;
	private String objetoInterno;
	private Logger log = Logger.getLogger(DateRangeValidator.class);

	public void initialize(DateRange dateRange) {
		start = dateRange.start();
		end = dateRange.end();
		entityClass = dateRange.entity();
		daoName = dateRange.daoName();
		tipo = dateRange.tipo();
		objetoInterno = dateRange.objetoInterno();
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public boolean isValid(BaseView objeto,
			ConstraintValidatorContext constraintValidatorContext) {
		try {

			Field field;
			Object value = null;
			Date startDate = null;
			Date endDate = null;
			boolean ret = false;
			Enum tipoObjeto = null;
			BaseView objetoInternoView = null;

			// validación sobre la fecha

			field = objeto.getClass().getDeclaredField(start);
			field.setAccessible(true);
			value = field.get(objeto);
			if (value != null && value instanceof Date) {
				startDate = (Date) value;
			} else {
				return false;
			}

			field = objeto.getClass().getDeclaredField(end);
			field.setAccessible(true);
			value = field.get(objeto);
			if (value != null && value instanceof Date) {
				endDate = (Date) value;
			} else {
				return false;
			}

			if (startDate.equals(endDate)) {
				ret = true;
			} else {
				ret = (startDate.before(endDate));
			}

			if (!"".equals(tipo)) {
				field = objeto.getClass().getDeclaredField(tipo);
				field.setAccessible(true);
				tipoObjeto = (Enum) field.get(objeto);
			}

			if (!"".equals(objetoInterno)) {
				field = objeto.getClass().getDeclaredField(objetoInterno);
				field.setAccessible(true);
				objetoInternoView = (BaseView) field.get(objeto);
			}

			// sólo valido contra la base si los datos estan correctos
			if (ret) {
				// validación sobre la base de datos
				dao = (BaseModelDAO<BaseModel>) FacesContextUtils
						.getWebApplicationContext(
								FacesContext.getCurrentInstance()).getBean(
								daoName);
				String query = null;
				List<Object> list;

				// verifico si existe o es un registro nuevo
				if (objeto.getId() == null) {

					if ("".equals(tipo)) {

						if (objetoInternoView == null) {
							query = String
									.format("from %s where ? between %s and %s  or ? between %s and %s",
											entityClass.getName(), start, end,
											start, end);
							list = (List<Object>) dao.find(query, startDate,
									endDate);
						} else {
							query = String
									.format("from %s where  %s = ? and (? between %s and %s  or ? between %s and %s)",
											entityClass.getName(),
											objetoInterno + ".id", start, end,
											start, end);
							list = (List<Object>) dao.find(query,
									objetoInternoView.getId(), startDate,
									endDate);
						}

					} else {
						if (objetoInternoView == null) {
							query = String
									.format("from %s where  %s = ? and (? between %s and %s  or ? between %s and %s)",
											entityClass.getName(), tipo, start,
											end, start, end);
							list = (List<Object>) dao.find(query, tipoObjeto,
									startDate, endDate);
						} else {
							query = String
									.format("from %s where  %s = ? and %s = ? and (? between %s and %s  or ? between %s and %s)",
											entityClass.getName(), tipo,
											objetoInterno + ".id", start, end,
											start, end);
							list = (List<Object>) dao.find(query, tipoObjeto,
									objetoInternoView.getId(), startDate,
									endDate);
						}
					}
				} else {

					if ("".equals(tipo)) {
						if (objetoInternoView == null) {
							query = String
									.format("from %s where (? between %s and %s  or ? between %s and %s) and id <> ?",
											entityClass.getName(), start, end,
											start, end);
							list = (List<Object>) dao.find(query, startDate,
									endDate, objeto.getId());
						} else {
							query = String
									.format("from %s where %s = ? and (? between %s and %s  or ? between %s and %s) and id <> ?",
											entityClass.getName(),
											objetoInterno + ".id", start, end,
											start, end);
							list = (List<Object>) dao.find(query,
									objetoInternoView.getId(), startDate,
									endDate, objeto.getId());
						}
					} else {
						if (objetoInternoView == null) {
							query = String
									.format("from %s where  %s = ? and (? between %s and %s  or ? between %s and %s) and id <> ?",
											entityClass.getName(), tipo, start,
											end, start, end);
							list = (List<Object>) dao.find(query, tipoObjeto,
									startDate, endDate, objeto.getId());
						} else {
							query = String
									.format("from %s where  %s = ? and  %s = ? and (? between %s and %s  or ? between %s and %s) and id <> ?",
											entityClass.getName(), tipo,
											objetoInterno + ".id", start, end,
											start, end);
							list = (List<Object>) dao.find(query, tipoObjeto,
									objetoInternoView.getId(), startDate,
									endDate, objeto.getId());
						}
					}

				}

				return list == null || list.size() == 0;

			}

		} catch (Throwable e) {
			log.error("Error al validar el objeto en el DateRangeValidator", e);
		}

		return false;
	}
}
